package com.studerb.nntp;

import static com.studerb.util.Constants.EVENT_SERVICE_NAME;

import java.net.InetAddress;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.net.ntp.NTPUDPClient;
import org.apache.commons.net.ntp.TimeStamp;
import org.apache.log4j.Logger;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.bushe.swing.event.annotation.EventSubscriber;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;

import com.studerb.events.ServerEvent;
import com.studerb.model.Server;
import com.studerb.service.NetworkMonitorService;
import com.studerb.service.interfaces.IServerService;
import com.studerb.service.interfaces.ISettingsService;

public class NetworkMonitorThread implements Runnable, ApplicationContextAware {
	private final Logger logger = Logger.getLogger(NetworkMonitorThread.class);
	@Autowired private NetworkMonitorService parent;
	@Autowired private IServerService serverService;
	@Autowired private ISettingsService settingsService;
	private NntpConnection nntpConnection;
	private AbstractApplicationContext appContext;
	private Server master;
	protected int SLEEP_TIME_UP;
	protected int SLEEP_TIME_DOWN;

	protected int CONNECT_TIMEOUT;
	protected boolean wasInterrupted = false;

	int currentNtpServerCount = 0;

	public void init() {
		this.SLEEP_TIME_UP = Integer.parseInt(this.settingsService.getSetting("networkMonitor.sleepTimeUp"));
		this.SLEEP_TIME_DOWN = Integer.parseInt(this.settingsService.getSetting("networkMonitor.sleepTimeDown"));
		this.CONNECT_TIMEOUT = Integer.parseInt(this.settingsService.getSetting("networkMonitor.connectTimeout"));

		this.master = this.serverService.getMasterServer();
		AnnotationProcessor.process(this);
		this.nntpConnection = (NntpConnection) this.appContext.getBean("nntpConnection");
	}

	@EventSubscriber(eventServiceName = EVENT_SERVICE_NAME) public void onEvent(ServerEvent serverEvent) {
		// ServerEvent.EventType eventType = serverEvent.getType();
		this.logger.debug("ServiceEvent");
		this.master = this.serverService.getMasterServer();
		if (this.master != null) {
			this.logger.info("Master Server Added - networkMonitor running");
		}
	}

	public void run() {
		this.logger.debug("Network Monitor Running...");
		while (true) {
			try {
				this.serverService.awaitMasterServer();
				this.parent.awaitNetworkDown();
				testConnectMasterServer();
				this.parent.setNetworkUp();
			}
			catch (InterruptedException interrupted) {
				this.logger.info("Interrupted: stopping network monitoring...");
				return;
			}
			catch (Exception exception) {
				this.logger.warn("Exception: " + ExceptionUtils.getRootCauseMessage(exception));
				try {
					Thread.sleep(this.SLEEP_TIME_DOWN);
				}
				catch (InterruptedException interrupted) {
					this.logger.info("Interrupted: stopping network monitoring...");
					return;
				}
			}
		}
	}

	@Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.appContext = (AbstractApplicationContext) applicationContext;
	}

	private void testConnectMasterServer() throws Exception {
		boolean oldAuthRequired;

		if (this.master == null) {
			throw new Exception("No master server");
		}

		InetAddress inetAddress = null;
		try {
			this.nntpConnection.setServer(this.master);
			this.nntpConnection.connect();
		}
		finally {

		}
	}

	private void testNtp() throws Exception {
		final String NTP_HOST = "north-america.pool.ntp.org";
		final int MAX_SERVER_COUNT = 5;

		InetAddress inetAddress = null;
		NTPUDPClient ntpClient = null;
		ntpClient = new NTPUDPClient();
		ntpClient.setDefaultTimeout(this.CONNECT_TIMEOUT);
		ntpClient.open();
		// et the hosts address if address is not yet set OR if we've tried this
		// host
		// too many times
		if (inetAddress == null || this.currentNtpServerCount >= MAX_SERVER_COUNT) {
			this.logger.debug("Getting host address via DNS");
			inetAddress = InetAddress.getByName(NTP_HOST);
			this.logger.debug("obtained new network address: " + inetAddress.getHostAddress() + " / " + inetAddress.getCanonicalHostName());
			this.currentNtpServerCount = 0;
		}
		TimeStamp xmitNtpTime = ntpClient.getTime(inetAddress).getMessage().getTransmitTimeStamp();
		this.logger.debug("Network Monitor: Transmit Timestamp:\t" + xmitNtpTime + "  " + xmitNtpTime.toDateString());
		++this.currentNtpServerCount;
	}
}
